home *** CD-ROM | disk | FTP | other *** search
/ Aminet 16 / Aminet 16 (1996)(GTI - Schatztruhe)[!][Dec 1996].iso / Aminet / util / misc / cookietool.lha / cookietool / cookietool.c < prev    next >
C/C++ Source or Header  |  1996-10-04  |  12KB  |  456 lines

  1. ; /*
  2. gcc cookietool.c -O -noixemul -o cookietool
  3. quit 0 ; */
  4. /*************************************************************************\
  5.           cookietool: remove duplicate entries from a cookie file
  6. Options allow sorting the output alphabetically, and reformatting the
  7. cookies to a given line length. The expected file format is plain text 
  8. with a "%%" line ending each cookie. 
  9. Usage:
  10.     cookietool [options] infile outfile
  11. options:      meaning:
  12.     -f        reformat, killing extra blanks
  13.     -f<len>   reformat to max. <len> characters per line
  14.     -b        cut two-liners in half when reformatting
  15.     -s0       sort output alphabetically
  16.     -s1        " by first word, i. e. ignoring leading spaces and such
  17.     -s2        " by first word *on the last line*
  18.     -s3        " by last word
  19.     -S[0-3]   case sensitive sorting
  20.     -x        extended search, removes "almost indentical"
  21.               items, too
  22. Limitations:
  23.     Input and output file must not be the same.
  24.     Options -x and -s/-S are mutual exclusive.
  25. \*************************************************************************/
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31.  
  32. char version[] = "$VER: cookietool 1.3 (03.10.96)";
  33.  
  34. long listsize = 1000;   /* will be adjusted dynamically */
  35. long listed = 0;
  36. int extended = 0, beauty = 0, case_sense = 0;
  37. int finalsort = 0, formatlen = 0;
  38.  
  39. struct cookie {
  40.   char *text;
  41.   char *sorthook;
  42.   long number;
  43. };
  44.  
  45. struct cookie *clist;
  46.  
  47. #define CBUFSIZE 20000
  48. #define LBUFSIZE 2000
  49. char cbuf[CBUFSIZE];    /* large enough to hold one complete cookie */
  50. char line[LBUFSIZE];    /* large enough to hold the longest line */
  51.  
  52.  
  53. void help(char *s)
  54. /* print a help text and nag about illegal parameter <s> */
  55. {
  56.   if (s) printf("illegal option '%s'\n", s);
  57.   printf("usage:  cookietool [options] <inputfile> <outputfile> \n");
  58.   printf("where options are:\n");
  59.   printf(" -x           extended search for 'almost identical' cookies, too\n");
  60.   printf(" -s[0|1|2|3]  sort output, looking at  -s0: first character,\n");
  61.   printf("              -s1: first alphanumeric, -s2: first alnum. on last line,\n");
  62.   printf("              -s3: last word in cookie.  (-s: same as -s1)\n");
  63.   printf(" -S[0|1|2|3]  case sensitive sorting\n");
  64.   printf(" -f<width>    reformat cookies to new text width (please, don't)\n");
  65.   printf(" -b           make 'beautiful' line breaks for two-liners\n");
  66.   printf(" -f           reformat, without changing line breaks\n");
  67. }
  68.  
  69.  
  70. int str_cmp(char *s, char *t)
  71. /* variation to strcmp(), can behave case-insensitive, if told to */
  72. {
  73.   if (case_sense) {
  74.     while (*s == *t) {
  75.       if (*s == '\0')  return 0;
  76.       s++; t++;
  77.     }
  78.     return (*s - *t);
  79.   } else {
  80.     while (toupper(*s) == toupper(*t)) {
  81.       if (*s == '\0')  return 0;
  82.       s++; t++;
  83.     }
  84.     return (toupper(*s) - toupper(*t));
  85.   }
  86. }
  87.  
  88.  
  89. void compress(char *s)
  90. /* covert to uppercase and remove extra blanks and punctuation */
  91. {
  92.   int onword=0;
  93.   char *s2=s;
  94.   char *home=s;
  95.   
  96.   while (*s2) {
  97.     if (isalnum(*s2)) {
  98.       *s++ = toupper(*s2);
  99.       onword = 1;
  100.     } else {
  101.       if (onword) *s++ = ' ';  /* ONE blank after each word */
  102.       onword = 0;
  103.     }
  104.     s2++;
  105.   }
  106.   if (!onword && s != home) s--;  /* remove the last trailing blank */
  107.   *s = '\0';
  108. }
  109.  
  110.  
  111. int reformat(char *s, int linelen)
  112. /* remove extra blanks and rearrange line breaks, return no. of lines */
  113. {
  114.   int onword=0;
  115.   int lines=0;
  116.   char *s2=s, *home=s;
  117.   char *lastspace=s-1, *lastbreak=s-1;
  118.   
  119.   while (*s2) {
  120.     if (isspace(*s2)) {
  121.       if (onword) {
  122.         *s++ = ' ';  /* ONE blank after each word */
  123.         if (ispunct(s2[-1]) && isspace(s2[1]))
  124.           *s++ = ' ';  /* allow a second space after a punctuation sign */
  125.       }
  126.       onword = 0;
  127.     } else {
  128.       *s++ = *s2;
  129.       onword = 1;
  130.     }
  131.     s2++;
  132.   }
  133.   if (s != home) {
  134.     if (!onword) s--;  /* remove the last trailing blank */
  135.     *s++ = '\n';   /* but add a LF */
  136.     *s = '\0';
  137.   } else {
  138.     *s = '\0';
  139.     return 0;   /* empty cookie */
  140.   }
  141.   /* now find out where to put line breaks: */
  142.   s = home; lines = 1;
  143.   while (*s) {
  144.     if (isspace(*s)) {
  145.       if (isspace(s[1])) s++;  /* two spaces */
  146.       if (s-lastbreak > linelen) {  /* line is getting too long */
  147.         if (lastbreak == lastspace)
  148.           lastspace = s;  /* oops, one single huge word on this line %-( */
  149.         *lastspace = '\n'; lastbreak = lastspace;
  150.         lines++;
  151.       } else
  152.         lastspace = s;
  153.     }
  154.     s++;
  155.   }
  156.   /* two-liners look better when split in the middle: */
  157.   if (lines == 2 && beauty) {
  158.     *lastbreak = ' ';
  159.     s = s2 = home + strlen(home)/2;
  160.     while (isgraph(*s) && isgraph(*s2)) {
  161.       s--; s2++;
  162.     }
  163.     if (isspace(*s))
  164.       *s = '\n';
  165.     else if (isspace(s2[1]))  /* two spaces */
  166.       s2[1] = '\n';
  167.     else
  168.       *s2 = '\n';
  169.   }
  170.   return lines;
  171. }
  172.  
  173. void read_cookies(FILE *fp)
  174. {
  175.   long cbuflen, ignored=0;
  176.   int lines;
  177.   unsigned char *s;
  178.  
  179.   printf("reading cookies"); fflush(stdout);
  180.   strcpy(cbuf,""); lines=0; cbuflen=0;
  181.   while (fgets(line,LBUFSIZE,fp)) {
  182.     if (strncmp(line,"%%",2)==0) {   /* "end of cookie"-marker */
  183.       if (lines>0) {       /* store the cookie */
  184.         if (extended) 
  185.           compress(cbuf);          /* well, store its shadow only ;-| */
  186.         else if (formatlen>0)
  187.           reformat(cbuf, formatlen);
  188.         cbuflen = strlen(cbuf)+1;
  189.         if (clist[listed].text = malloc(cbuflen)) {
  190.           clist[listed].number = listed+ignored;
  191.           strcpy(clist[listed].text, cbuf);
  192.           s = clist[listed].sorthook = clist[listed].text;
  193.         } else {
  194.           printf("\nout of memory\n");
  195.           exit(20);
  196.         }
  197.         if (++listed == listsize) {
  198.           listsize = 3 * listsize / 2;
  199.           clist = realloc(clist, listsize * sizeof(struct cookie));
  200.           if (!clist) {
  201.             printf("\nlist reallocation failed\n");
  202.             exit(20);
  203.           }
  204.         }
  205.       } else {
  206.         ignored++;   /* or ignore it */
  207.       }
  208.       /* start a new one */
  209.       strcpy(cbuf,""); lines=0; cbuflen=0;
  210.     } else {
  211.       if (formatlen<0)
  212.         reformat(line, LBUFSIZE);
  213.       if ((cbuflen += strlen(line)) >= CBUFSIZE) {
  214.         printf("\ncookie too big (>%ld chars)\n", CBUFSIZE);
  215.         exit(20);
  216.       }
  217.       strcat(cbuf,line); lines++;
  218.     }
  219.   }
  220.   printf(", done. (%ld read, %ld empty)\n", listed, ignored);
  221. }
  222.  
  223.  
  224. void set_hooks()
  225. /* adjust sorthooks for the final sort, according to the desired mode */
  226. {
  227.   long l;
  228.   int hot;
  229.   char *s;
  230.   
  231.   printf("adjusting sort hooks"); fflush(stdout);
  232.   for (l=0; l<listed; l++) {
  233.     s = clist[l].text;
  234.     switch (finalsort) {
  235.       case 2:   /* get a pointer to the first alphanumeric */
  236.         while (*s && !isalnum(*s))  s++;
  237.         clist[l].sorthook = s; break;
  238.       case 3:   /* first alphanumeric on the last line */
  239.         hot = 1;
  240.         while (*s) {
  241.           if (*s == '\n')  hot = 1;
  242.           if (isalnum(*s) && hot) {
  243.             clist[l].sorthook = s; hot = 0;
  244.           }
  245.           s++;
  246.         } break;
  247.       case 4:   /* first alphanumeric in the last word */
  248.         hot = 1;
  249.         while (*s) {
  250.           if (!isalnum(*s))
  251.             hot = 1;
  252.           else if (hot) {
  253.             clist[l].sorthook = s; hot = 0;
  254.           }
  255.           s++;
  256.         } break;
  257.       default:
  258.     }
  259.   }
  260.   printf(", done.\n");
  261. }
  262.  
  263.  
  264. void write_cookies(FILE *fp)
  265. /* also frees the allocated memory! */
  266. {
  267.   long l;
  268.   
  269.   printf("writing cookies"); fflush(stdout);
  270.   for (l=0; l<listed; l++) {
  271.     fputs(clist[l].text, fp);
  272.     fputs("%%\n", fp);
  273.     free(clist[l].text);
  274.   }
  275.   printf(", done.\n");
  276. }
  277.  
  278.  
  279. void filter_cookies(FILE *fp1, FILE *fp2)
  280. /* combination of read_cookies()/write_cookies() */
  281. {
  282.   long number=0, index=0;
  283.  
  284.   rewind(fp1);
  285.   printf("copying cookies"); fflush(stdout);
  286.   strcpy(cbuf,"");
  287.   while (fgets(line,LBUFSIZE,fp1) && index<listed) {
  288.     if (strncmp(line,"%%",2)==0) {   /* "end of cookie"-marker */
  289.       if (number == clist[index].number) {    /* "good" cookie */
  290.         if (formatlen>0)
  291.           reformat(cbuf, formatlen);
  292.         fputs(cbuf, fp2);                          /* copy it */
  293.         fputs("%%\n", fp2);
  294.         free(clist[index++].text);
  295.       }
  296.       /* start a new one */
  297.       number++;
  298.       strcpy(cbuf,"");
  299.     } else {
  300.       if (formatlen<0)
  301.         reformat(line, LBUFSIZE);
  302.       /* no test for "monster cookies" needed in the 2nd pass */
  303.       strcat(cbuf,line);
  304.     }
  305.   }
  306.   printf(", done. (%ld copied)\n", index);
  307. }
  308.  
  309.  
  310. void sift(struct cookie v[], long i, long m, int mode)
  311. /* centre routine to heapsort() */
  312. /* mode==0: sort by name, mode==1: sort by number */
  313. {
  314.   long j;
  315.   struct cookie temp;
  316.  
  317.   if (mode==0) {  /* by name */
  318.     while ((j = 2*(i+1)-1) <= m) {
  319.       if (j < m  && (str_cmp(v[j].sorthook, v[j+1].sorthook) < 0) )
  320.         j++;
  321.       if (str_cmp(v[i].sorthook, v[j].sorthook) < 0) {
  322.         temp = v[i]; v[i] = v[j]; v[j] = temp;
  323.         i = j;
  324.       } else
  325.         i = m;  /* done */
  326.     }
  327.   } else {  /* by number */
  328.     while ((j = 2*(i+1)-1) <= m) {
  329.       if (j < m  && (v[j].number < v[j+1].number) )
  330.         j++;
  331.       if (v[i].number < v[j].number) {
  332.         temp = v[i]; v[i] = v[j]; v[j] = temp;
  333.         i = j;
  334.       } else
  335.         i = m;  /* done */
  336.     }
  337.   }
  338. }
  339.  
  340.  
  341. void my_heapsort(struct cookie v[], long n, int mode)
  342. /* mode==0: sort by name, mode==1: sort by number */
  343. {
  344.   long i;
  345.   struct cookie temp;
  346.  
  347.   if (n<2)          /* no sorting necessary */
  348.     return;
  349.   for (i = n/2-1; i >= 0; i--)
  350.     sift(v, i, n-1, mode);
  351.   for (i = n-1; i >= 1; i--) {
  352.     temp = v[0]; v[0] = v[i]; v[i] = temp;
  353.     sift(v, 0, i-1, mode);
  354.   }
  355. }
  356.  
  357.  
  358. void one_cookie(void)
  359. {
  360.   long dbl = 0;
  361.   long i, j;
  362.   
  363.   printf("removing double entries"); fflush(stdout);
  364.   my_heapsort(clist, listed, 0);
  365.   for (i = listed-1; i > 0; i = j)
  366.     for (j = i-1; j >= 0 && !str_cmp(clist[i].text,clist[j].text); j--) {
  367.       free(clist[j].text);
  368.       clist[j] = clist[--listed];
  369.       dbl++;
  370.     }
  371.   printf(", done. (%ld found)\n",dbl);
  372.   if (finalsort) {
  373.     if (finalsort>1) set_hooks();
  374.     printf("sorting"); fflush(stdout);
  375.     my_heapsort(clist, listed, 0);
  376.   } else {
  377.     printf("restoring order"); fflush(stdout);
  378.     my_heapsort(clist, listed, 1);
  379.   }
  380.   printf(", done.\n");
  381. }
  382.  
  383.  
  384. int main(int argc, char *argv[])
  385. {
  386.   char *s;
  387.   char name1[100], name2[100];
  388.   FILE *infile, *outfile;
  389.   
  390.   name1[0] = name2[0] = '\0';
  391.   if (argc<2) {
  392.     help(NULL);
  393.     return 5;
  394.   }
  395.   while (--argc) {
  396.     s = *++argv;
  397.     if (*s != '-') {
  398.       if (name1[0] == '\0')
  399.         strcpy(name1, s);
  400.       else
  401.         strcpy(name2, s);
  402.     } else {
  403.       switch (*++s) {
  404.         case 'f':
  405.           formatlen = -1;      /* flag to reformat line by line only */
  406.           if (isdigit(*++s)) formatlen = atoi(s);   /* cookie by cookie */
  407.           break;
  408.         case 'b':
  409.           beauty = 1; break;
  410.         case 's': case 'S':
  411.           if (extended)
  412.             printf("warning: -x disabled by %s\n", argv[0]);
  413.           extended = 0; case_sense = (*s == 'S'); finalsort = 2;
  414.           if (isdigit(*++s)) finalsort = atoi(s) + 1;
  415.           break;
  416.         case 'x':
  417.           if (finalsort)
  418.             printf("warning: -s/-S disabled by -x\n");
  419.           finalsort = 0; extended = 1; break;
  420.         default:
  421.           help(argv[0]); return 5;
  422.       }
  423.     }
  424.   }
  425.   if (name1[0] == '\0') {
  426.     help(NULL);
  427.     return 5;
  428.   }
  429.   if (name2[0] == '\0') {
  430.     strcpy(name2,name1);
  431.     strcat(name2,".crunch");
  432.   } 
  433.   if (!(infile = fopen(name1,"r"))) {
  434.     printf("Can't open %s for input!\n", name1);
  435.     return 10;
  436.   }
  437.   if (!(outfile = fopen(name2,"w"))) {
  438.     printf("Can't open %s for output!\n", name2);
  439.     return 10;
  440.   }
  441.   clist = malloc(listsize * sizeof(struct cookie));
  442.   if (!clist) {
  443.     printf("list allocation failed\n");
  444.     exit(20);
  445.   }
  446.   read_cookies(infile);
  447.   one_cookie();
  448.   if (extended)
  449.     filter_cookies(infile,outfile);
  450.   else
  451.     write_cookies(outfile);
  452.   free(clist);
  453.   return 0;
  454. }
  455.  
  456.